package com.bplow.deep.base.concurrent.synclock;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;

import com.bplow.deep.base.redis.RedisTool;

import redis.clients.jedis.Jedis;

@SuppressWarnings("serial")
public class SyncLockInterceptor implements MethodInterceptor, Serializable {

	Logger logger = LoggerFactory.getLogger(getClass());

	private SyncLockAttributeSource syncLockAttributeSource;


	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		Method method = invocation.getMethod();

		Invoker aopAllianceInvoker = new Invoker() {
			public Object invoke() {
				try {
					return invocation.proceed();
				} catch (Throwable ex) {
					throw new ThrowableWrapper(ex);
				}
			}
		};

		try {
			return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
		} catch (ThrowableWrapper th) {
			throw th.original;
		}
	}

	protected Object execute(Invoker invoker, Object target, Method method, Object[] args) {
		Jedis jedis = new Jedis("192.168.89.80");
		// get backing class
		Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
		if (targetClass == null && target != null) {
			targetClass = target.getClass();
		}
		SyncLockAttribute syncLockAttribute = getSyncLockAttributeSource().getSyncLockAttribute(method, targetClass);
		String requestId = UUID.randomUUID().toString();
		logger.info("请求获取锁：{}", requestId);

		boolean lock = false;
		Object obj = null;

		try {

			long maxSleepTime = System.currentTimeMillis() + syncLockAttribute.getMaxSleepMills();
            System.out.println(lock);
			while (!lock) {

				// 获取锁
				lock = RedisTool.tryGetDistributedLock(jedis, syncLockAttribute.getLockId(), requestId,
						Long.valueOf(syncLockAttribute.getKeepMills()).intValue());

				if (lock) {
					obj = invoker.invoke();
				}
				// 不设置锁的超时时间
				else if (syncLockAttribute.getKeepMills() <= 0) {
					if (syncLockAttribute.isWait()) {
						if (syncLockAttribute.getMaxSleepMills() > 0 && System.currentTimeMillis() > maxSleepTime) {
							throw new TimeoutException("获取锁资源等待超时");
						}
						TimeUnit.MILLISECONDS.sleep(syncLockAttribute.getSleepMills());
					} else {
						break;
					}
				} else {// 等待

					// 继续等待获取锁
					if (syncLockAttribute.isWait()) {
						// 如果超过最大等待时间抛出异常
						if (syncLockAttribute.getMaxSleepMills() > 0 && System.currentTimeMillis() > maxSleepTime) {
							throw new TimeoutException("获取锁资源等待超时");
						}
						TimeUnit.MILLISECONDS.sleep(syncLockAttribute.getSleepMills());
					}
					// 放弃等待
					else {
						break;
					}

				}

			}

		} catch (Exception e) {
			logger.error("", e);
		}

		finally {
			// 释放锁
			boolean result = RedisTool.releaseDistributedLock(jedis, syncLockAttribute.getLockId(), requestId);
			logger.info("释放锁{},结果{}",syncLockAttribute.getLockId(),result);
			jedis.close();
		}

		return obj;
	}

	public interface Invoker {

		Object invoke();
	}

	private static class ThrowableWrapper extends RuntimeException {
		private final Throwable original;

		ThrowableWrapper(Throwable original) {
			this.original = original;
		}
	}

	public SyncLockAttributeSource getSyncLockAttributeSource() {
		return syncLockAttributeSource;
	}

	public void setSyncLockAttributeSource(SyncLockAttributeSource syncLockAttributeSource) {
		this.syncLockAttributeSource = syncLockAttributeSource;
	}

}
